前言

服务提供者是 laravel 框架的重要组成部分,承载着各种服务,自定义的应用以及所有 Laravel 的核心服务都是通过服务提供者启动。本文将会介绍服务提供者的源码分析,关于服务提供者的使用,请参考官方文档 :服务提供者

服务提供者的注册

服务提供者的启动由类 \Illuminate\Foundation\Bootstrap\RegisterProviders::class 负责,该类用于加载所有服务提供者的 register 函数,并保存延迟加载的服务的信息,以便实现延迟加载。

  1. class RegisterProviders
  2. {
  3. public function bootstrap(Application $app)
  4. {
  5. $app->registerConfiguredProviders();
  6. }
  7. }
  8. class Application extends Container implements ApplicationContract, HttpKernelInterface
  9. {
  10. public function registerConfiguredProviders()
  11. {
  12. (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
  13. ->load($this->config['app.providers']);
  14. }
  15. public function getCachedServicesPath()
  16. {
  17. return $this->bootstrapPath().'/cache/services.php';
  18. }
  19. }

以上可以看出,所有服务提供者都在配置文件 app.php 文件的 providers 数组中。类 ProviderRepository 负责所有的服务加载功能:

  1. class ProviderRepository
  2. {
  3. public function load(array $providers)
  4. {
  5. $manifest = $this->loadManifest();
  6. if ($this->shouldRecompile($manifest, $providers)) {
  7. $manifest = $this->compileManifest($providers);
  8. }
  9. foreach ($manifest['when'] as $provider => $events) {
  10. $this->registerLoadEvents($provider, $events);
  11. }
  12. foreach ($manifest['eager'] as $provider) {
  13. $this->app->register($provider);
  14. }
  15. $this->app->addDeferredServices($manifest['deferred']);
  16. }
  17. }

加载服务缓存文件

laravel 会把所有的服务整理起来,作为缓存写在缓存文件中:

  1. return array (
  2. 'providers' =>
  3. array (
  4. 0 => 'Illuminate\\Auth\\AuthServiceProvider',
  5. 1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
  6. ...
  7. ),
  8. 'eager' =>
  9. array (
  10. 0 => 'Illuminate\\Auth\\AuthServiceProvider',
  11. 1 => 'Illuminate\\Cookie\\CookieServiceProvider',
  12. ...
  13. ),
  14. 'deferred' =>
  15. array (
  16. 'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
  17. 'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
  18. ...
  19. ),
  20. 'when' =>
  21. array (
  22. 'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
  23. array (
  24. ),
  25. ...
  26. ),
  • 缓存文件中 providers 放入了所有自定义和框架核心的服务。
  • eager 数组中放入了所有需要立即启动的服务提供者。
  • deferred 数组中放入了所有需要延迟加载的服务提供者。
  • when 放入了延迟加载需要激活的事件。

加载服务提供者缓存文件:

  1. public function loadManifest()
  2. {
  3. if ($this->files->exists($this->manifestPath)) {
  4. $manifest = $this->files->getRequire($this->manifestPath);
  5. if ($manifest) {
  6. return array_merge(['when' => []], $manifest);
  7. }
  8. }
  9. }

编译服务提供者

laravel 中的服务提供者没有缓存文件或者有变动,那么就会重新生成缓存文件:

  1. public function shouldRecompile($manifest, $providers)
  2. {
  3. return is_null($manifest) || $manifest['providers'] != $providers;
  4. }
  5. protected function compileManifest($providers)
  6. {
  7. $manifest = $this->freshManifest($providers);
  8. foreach ($providers as $provider) {
  9. $instance = $this->createProvider($provider);
  10. if ($instance->isDeferred()) {
  11. foreach ($instance->provides() as $service) {
  12. $manifest['deferred'][$service] = $provider;
  13. }
  14. $manifest['when'][$provider] = $instance->when();
  15. }
  16. else {
  17. $manifest['eager'][] = $provider;
  18. }
  19. }
  20. return $this->writeManifest($manifest);
  21. }
  22. protected function freshManifest(array $providers)
  23. {
  24. return ['providers' => $providers, 'eager' => [], 'deferred' => []];
  25. }
  • 如果服务提供者是需要立即注册的,那么将会放入缓存文件中 eager 数组中。
  • 如果服务提供者是延迟加载的,那么其函数 provides() 通常会提供服务别名,这个服务别名通常是向服务容器中注册的别名,别名将会放入缓存文件的 deferred 数组中。
  • 延迟加载若有 event 事件激活,那么可以在 when 函数中写入事件类,并写入缓存文件的 when 数组中。

延迟服务提供者事件注册

延迟服务提供者除了利用 IOC 容器解析服务方式激活,还可以利用 Event 事件来激活:

  1. protected function registerLoadEvents($provider, array $events)
  2. {
  3. if (count($events) < 1) {
  4. return;
  5. }
  6. $this->app->make('events')->listen($events, function () use ($provider) {
  7. $this->app->register($provider);
  8. });
  9. }

注册即时启动的服务提供者

服务提供者的注册函数 register() 由类 Application 来调用:

  1. class Application extends Container implements ApplicationContract, HttpKernelInterface
  2. {
  3. public function register($provider, $options = [], $force = false)
  4. {
  5. if (($registered = $this->getProvider($provider)) && ! $force) {
  6. return $registered;
  7. }
  8. if (is_string($provider)) {
  9. $provider = $this->resolveProvider($provider);
  10. }
  11. if (method_exists($provider, 'register')) {
  12. $provider->register();
  13. }
  14. $this->markAsRegistered($provider);
  15. if ($this->booted) {
  16. $this->bootProvider($provider);
  17. }
  18. return $provider;
  19. }
  20. public function getProvider($provider)
  21. {
  22. $name = is_string($provider) ? $provider : get_class($provider);
  23. return Arr::first($this->serviceProviders, function ($value) use ($name) {
  24. return $value instanceof $name;
  25. });
  26. }
  27. public function resolveProvider($provider)
  28. {
  29. return new $provider($this);
  30. }
  31. protected function markAsRegistered($provider)
  32. {
  33. $this->serviceProviders[] = $provider;
  34. $this->loadedProviders[get_class($provider)] = true;
  35. }
  36. protected function bootProvider(ServiceProvider $provider)
  37. {
  38. if (method_exists($provider, 'boot')) {
  39. return $this->call([$provider, 'boot']);
  40. }
  41. }
  42. }

可以看出,服务提供者的注册过程:

  • 判断当前服务提供者是否被注册过,如注册过直接返回对象
  • 解析服务提供者
  • 调用服务提供者的 register 函数
  • 标记当前服务提供者已经注册完毕
  • 若框架已经加载注册完毕所有的服务容器,那么就启动服务提供者的 boot 函数,该函数由于是 call 调用,所以支持依赖注入。

延迟服务提供者激活与注册

延迟服务提供者首先需要添加到 Application 中:

  1. public function addDeferredServices(array $services)
  2. {
  3. $this->deferredServices = array_merge($this->deferredServices, $services);
  4. }

我们之前说过,延迟服务提供者的激活注册有两种方法:事件与服务解析。

当特定的事件被激发后,就会调用 Applicationregister 函数,进而调用服务提供者的 register 函数,实现服务的注册。

当利用 Ioc 容器解析服务名时,例如解析服务名 BroadcastingFactory

  1. class BroadcastServiceProvider extends ServiceProvider
  2. {
  3. protected $defer = true;
  4. public function provides()
  5. {
  6. return [
  7. BroadcastManager::class,
  8. BroadcastingFactory::class,
  9. BroadcasterContract::class,
  10. ];
  11. }
  12. }
  1. public function make($abstract)
  2. {
  3. $abstract = $this->getAlias($abstract);
  4. if (isset($this->deferredServices[$abstract])) {
  5. $this->loadDeferredProvider($abstract);
  6. }
  7. return parent::make($abstract);
  8. }
  9. public function loadDeferredProvider($service)
  10. {
  11. if (! isset($this->deferredServices[$service])) {
  12. return;
  13. }
  14. $provider = $this->deferredServices[$service];
  15. if (! isset($this->loadedProviders[$provider])) {
  16. $this->registerDeferredProvider($provider, $service);
  17. }
  18. }

deferredServices 数组可以得知,BroadcastingFactory 为延迟服务,接着程序会利用函数 loadDeferredProvider 来加载延迟服务提供者,调用服务提供者的 register 函数,若当前的框架还未注册完全部服务。那么将会放入服务启动的回调函数中,以待服务启动时调用:

  1. public function registerDeferredProvider($provider, $service = null)
  2. {
  3. if ($service) {
  4. unset($this->deferredServices[$service]);
  5. }
  6. $this->register($instance = new $provider($this));
  7. if (! $this->booted) {
  8. $this->booting(function () use ($instance) {
  9. $this->bootProvider($instance);
  10. });
  11. }
  12. }

关于服务提供者的注册函数:

  1. class BroadcastServiceProvider extends ServiceProvider
  2. {
  3. protected $defer = true;
  4. public function register()
  5. {
  6. $this->app->singleton(BroadcastManager::class, function ($app) {
  7. return new BroadcastManager($app);
  8. });
  9. $this->app->singleton(BroadcasterContract::class, function ($app) {
  10. return $app->make(BroadcastManager::class)->connection();
  11. });
  12. $this->app->alias(
  13. BroadcastManager::class, BroadcastingFactory::class
  14. );
  15. }
  16. public function provides()
  17. {
  18. return [
  19. BroadcastManager::class,
  20. BroadcastingFactory::class,
  21. BroadcasterContract::class,
  22. ];
  23. }
  24. }

函数 register 为类 BroadcastingFactoryIoc 容器绑定了特定的实现类 BroadcastManager,这样 Ioc 容器中的 make 函数:

  1. public function make($abstract)
  2. {
  3. $abstract = $this->getAlias($abstract);
  4. if (isset($this->deferredServices[$abstract])) {
  5. $this->loadDeferredProvider($abstract);
  6. }
  7. return parent::make($abstract);
  8. }

parent::make($abstract) 就会正确的解析服务 BroadcastingFactory

因此函数 provides() 返回的元素一定都是 register()IOC 容器中绑定的类名或者别名。这样当我们利用服务容器来利用 App::make() 解析这些类名的时候,服务容器才会根据服务提供者的 register() 函数中绑定的实现类,从而正确解析服务功能。

服务容器的启动

服务容器的启动由类 \Illuminate\Foundation\Bootstrap\BootProviders::class 负责:

  1. class BootProviders
  2. {
  3. public function bootstrap(Application $app)
  4. {
  5. $app->boot();
  6. }
  7. }
  8. class Application extends Container implements ApplicationContract, HttpKernelInterface
  9. {
  10. public function boot()
  11. {
  12. if ($this->booted) {
  13. return;
  14. }
  15. $this->fireAppCallbacks($this->bootingCallbacks);
  16. array_walk($this->serviceProviders, function ($p) {
  17. $this->bootProvider($p);
  18. });
  19. $this->booted = true;
  20. $this->fireAppCallbacks($this->bootedCallbacks);
  21. }
  22. protected function bootProvider(ServiceProvider $provider)
  23. {
  24. if (method_exists($provider, 'boot')) {
  25. return $this->call([$provider, 'boot']);
  26. }
  27. }
  28. }